🎯 Lab Objectives

What You'll Learn Today

  • Understand Object-Oriented Programming (OOP) concepts
  • Create classes and objects in Python
  • Implement constructors and methods
  • Master inheritance and method overriding
  • Learn operator overloading for custom behavior
  • Build real-world applications using OOP

📚 Understanding Classes and Objects

🔍 What is a Class?

A class is a blueprint or template for creating objects. It defines:

  • Attributes: Properties/characteristics of objects
  • Methods: Behaviors/actions that objects can perform
  • Constructor: Special method to initialize objects

🏗️ What is an Object?

An object is an instance of a class. It has:

  • State: Current values of attributes
  • Behavior: Ability to perform methods
  • Identity: Unique memory address

Class vs Object Relationship

Class: Car (Blueprint)
├── Attributes: color, brand, model, year
├── Methods: start(), stop(), accelerate()
└── Constructor: __init__()

Objects (Instances):
├── car1: Red, Toyota, Camry, 2022
├── car2: Blue, Honda, Civic, 2021
└── car3: Black, Tesla, Model 3, 2023

📝 Task 1: Basic Student Class

🎯 Problem Statement

Create a Student class with:

  • Attributes: name, sap_id, marks (physics, chemistry, maths)
  • Create 3 objects with user input
  • Display details of all students

💻 Solution

class Student: def __init__(self, name, sap_id, physics, chemistry, maths): self.name = name self.sap_id = sap_id self.marks = { 'physics': physics, 'chemistry': chemistry, 'maths': maths } def display(self): print(f"Name: {self.name}") print(f"SAP ID: {self.sap_id}") print("Marks:") for subject, mark in self.marks.items(): print(f" {subject}: {mark}") # Create 3 student objects students = [] for i in range(3): print(f"\n--- Enter Student {i+1} Details ---") name = input("Enter name: ") sap_id = input("Enter SAP ID: ") physics = float(input("Enter Physics marks: ")) chemistry = float(input("Enter Chemistry marks: ")) maths = float(input("Enter Maths marks: ")) student = Student(name, sap_id, physics, chemistry, maths) students.append(student) # Display all student details print("\n=== All Student Details ===") for i, student in enumerate(students, 1): print(f"\n--- Student {i} ---") student.display()
SAMPLE OUTPUT:
--- Enter Student 1 Details ---
Enter name: Alice
Enter SAP ID: 50001234
Enter Physics marks: 85
Enter Chemistry marks: 78
Enter Maths marks: 92

--- Enter Student 2 Details ---
Enter name: Bob
Enter SAP ID: 50001235
Enter Physics marks: 76
Enter Chemistry marks: 88
Enter Maths marks: 81

--- Enter Student 3 Details ---
Enter name: Charlie
Enter SAP ID: 50001236
Enter Physics marks: 90
Enter Chemistry marks: 85
Enter Maths marks: 87

=== All Student Details ===

--- Student 1 ---
Name: Alice
SAP ID: 50001234
Marks:
  physics: 85
  chemistry: 78
  maths: 92

📝 Task 2: Enhanced Student Class with Methods

🎯 Problem Statement

Enhance the Student class with:

  • Constructor to initialize n students
  • display() method to show student details
  • find_marks_percentage() method for each student
  • display_result() method (Pass if each subject >40%, else Fail)
  • Function to find class average

💻 Solution

class StudentEnhanced: def __init__(self, name, sap_id, physics, chemistry, maths): self.name = name self.sap_id = sap_id self.marks = { 'physics': physics, 'chemistry': chemistry, 'maths': maths } def display(self): print(f"Name: {self.name}") print(f"SAP ID: {self.sap_id}") print("Marks:") for subject, mark in self.marks.items(): print(f" {subject}: {mark}") def find_marks_percentage(self): # Assuming each subject is out of 100 total_marks = sum(self.marks.values()) percentage = (total_marks / 300) * 100 return percentage def display_result(self): percentage = self.find_marks_percentage() print(f"Percentage: {percentage:.2f}%") # Check if each subject > 40% passed_all = all(mark > 40 for mark in self.marks.values()) if passed_all: print("Result: PASS 🎉") else: print("Result: FAIL ❌") # Show failed subjects failed_subjects = [subject for subject, mark in self.marks.items() if mark <= 40] print(f"Failed subjects: {', '.join(failed_subjects)}") def class_average(students): # Calculate average percentage of entire class if not students: return 0 total_percentage = sum(student.find_marks_percentage() for student in students) average = total_percentage / len(students) return average # Example usage students = [] for i in range(3): print(f"\n--- Enter Student {i+1} Details ---") name = input("Enter name: ") sap_id = input("Enter SAP ID: ") physics = float(input("Enter Physics marks: ")) chemistry = float(input("Enter Chemistry marks: ")) maths = float(input("Enter Maths marks: ")) student = StudentEnhanced(name, sap_id, physics, chemistry, maths) students.append(student) # Display individual results for i, student in enumerate(students, 1): print(f"\n--- Student {i} ---") student.display() student.display_result() # Calculate and display class average avg = class_average(students) print(f"\n=== Class Average: {avg:.2f}% ===")

🧬 Task 3: Types of Inheritance

🔍 What is Inheritance?

Inheritance is a mechanism where a new class derives attributes and methods from an existing class.

  • Parent Class (Base Class): Class being inherited from
  • Child Class (Derived Class): Class that inherits
  • Benefits: Code reusability, hierarchical organization

Types of Inheritance

1. Single Inheritance:
class Animal → class Dog

2. Multiple Inheritance:
class Father + class Mother → class Child

3. Multilevel Inheritance:
class Grandparent → class Parent → class Child

4. Hierarchical Inheritance:
class Parent → class Child1 + class Child2 + class Child3

5. Hybrid Inheritance:
Combination of multiple types

💻 Implementation Examples

# 1. Single Inheritance class Animal: def speak(self): print("Animal makes a sound") class Dog(Animal): def speak(self): print("Dog barks: Woof!") # 2. Multiple Inheritance class Father: def skills(self): print("Father: Technical skills") class Mother: def skills(self): print("Mother: Creative skills") class Child(Father, Mother): def skills(self): print("Child: Both technical and creative skills") # 3. Multilevel Inheritance class Grandparent: def property(self): print("Grandparent: Wisdom") class Parent(Grandparent): def property(self): print("Parent: Experience") class Child(Parent): def property(self): print("Child: Energy") # Test inheritance print("=== Single Inheritance ===") dog = Dog() dog.speak() print("\n=== Multiple Inheritance ===") child = Child() child.skills() print("\n=== Multilevel Inheritance ===") child = Child() child.property()

🔄 Task 4: Method Overriding

🔍 What is Method Overriding?

Method Overriding occurs when a child class provides a specific implementation of a method that is already defined in its parent class.

  • Child class method replaces parent class method
  • Same method name and parameters
  • Different implementation in child class
  • Enables polymorphism - same interface, different behavior

💻 Implementation Example

class Shape: def area(self): print("Calculating area of generic shape") return 0 class Rectangle(Shape): def __init__(self, width, height): self.width = width self.height = height def area(self): # Overriding parent's area method print("Calculating area of rectangle") return self.width * self.height class Circle(Shape): def __init__(self, radius): self.radius = radius def area(self): # Overriding parent's area method print("Calculating area of circle") return 3.14159 * self.radius ** 2 class Triangle(Shape): def __init__(self, base, height): self.base = base self.height = height def area(self): # Overriding parent's area method print("Calculating area of triangle") return 0.5 * self.base * self.height # Test method overriding shapes = [ Rectangle(5, 4), Circle(3), Triangle(6, 8) ] print("=== Method Overriding Examples ===") for i, shape in enumerate(shapes, 1): print(f"\nShape {i}:") area = shape.area() print(f"Area: {area:.2f}")

➕ Task 5: Operator Overloading

🔍 What is Operator Overloading?

Operator Overloading allows you to define how operators behave with your custom objects.

  • Redefine standard operators (+, -, *, /, etc.)
  • Make objects work with built-in operators
  • Implement using magic methods (dunder methods)
  • Creates intuitive object behavior

Common Operator Overloading Methods

Arithmetic Operators:
__add__ : + operator
__sub__ : - operator
__mul__ : * operator
__truediv__ : / operator

Comparison Operators:
__eq__ : == operator
__lt__ : < operator
__gt__ : > operator

String Representation:
__str__ : str() function
__repr__ : repr() function

💻 Point Class with + Operator Overloading

class Point: def __init__(self, x, y): self.x = x self.y = y def __add__(self, other): # Overload + operator if isinstance(other, Point): new_x = self.x + other.x new_y = self.y + other.y return Point(new_x, new_y) else: raise TypeError("Can only add Point to Point") def __str__(self): # String representation return f"Point(x={self.x}, y={self.y})" def __repr__(self): # Official string representation return self.__str__() # Create example points from problem p1 = Point(10, 20) p2 = Point(12, 15) # Add points using overloaded + operator p3 = p1 + p2 print(f"P1: {p1}") print(f"P2: {p2}") print(f"P3 = P1 + P2: {p3}") # Test with different operations print("\n=== Additional Examples ===") class Vector: def __init__(self, x, y): self.x = x self.y = y def __add__(self, other): return Vector(self.x + other.x, self.y + other.y) def __mul__(self, scalar): # Overload * operator for scalar multiplication return Vector(self.x * scalar, self.y * scalar) def __str__(self): return f"Vector({self.x}, {self.y})" v1 = Vector(3, 4) v2 = Vector(1, 2) # Vector addition v3 = v1 + v2 print(f"Vector addition: {v1} + {v2} = {v3}") # Scalar multiplication v4 = v1 * 2 print(f"Scalar multiplication: {v1} * 2 = {v4}")